package com.chuangke.mds.service.impl;

import java.io.IOException;
import java.nio.file.Path;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.chuangke.admin.data.builder.DeptDataBuilder;
import com.chuangke.admin.data.builder.DictDataBuilder;
import com.chuangke.admin.entity.SysLog;
import com.chuangke.admin.entity.SysUser;
import com.chuangke.common.data.builder.DataBuildConfig;
import com.chuangke.common.data.builder.DataBuilder;
import com.chuangke.mds.entity.Knowledge;
import com.chuangke.system.dao.SysRelationDao;
import com.chuangke.system.entity.SysAttachment;
import com.chuangke.system.entity.SysRelation;
import com.chuangke.system.service.SysAttachmentService;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.*;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.chuangke.common.constant.ColumnConstants;
import com.chuangke.common.constant.FileConstants;
import com.chuangke.common.constant.Masks;
import com.chuangke.common.constant.SysBillConstants;
import com.chuangke.common.db.page.CkPageQuery;
import com.chuangke.common.enums.OrderStatusEnum;
import com.chuangke.common.exception.ChuangkeException;
import com.chuangke.common.idgen.IdManager;
import com.chuangke.common.trans.annotation.TransResult;
import com.chuangke.common.utils.DateUtils;
import com.chuangke.common.utils.SecurityUtils;
import com.chuangke.context.service.ContextService;
import com.chuangke.file.model.FileInfo;
import com.chuangke.file.service.FileTransferService;
import com.chuangke.flow.domain.FlowFormModel;
import com.chuangke.flow.service.FlowInstanceService;
import com.chuangke.mds.dao.TechnologyProcessDao;
import com.chuangke.mds.dao.TechnologyProcessProcedureDao;
import com.chuangke.mds.dao.TechnologyProcessProcedureNodeDao;
import com.chuangke.mds.entity.TechnologyProcess;
import com.chuangke.mds.entity.TechnologyProcessProcedure;
import com.chuangke.mds.entity.TechnologyProcessProcedureNode;
import com.chuangke.mds.service.TechnologyProcessService;
import com.chuangke.system.entity.SysConfig;
import com.chuangke.system.service.SysConfigService;

import cn.hutool.core.util.StrUtil;

@Service
@Transactional(rollbackFor = Throwable.class)
public class TechnologyProcessImpl implements TechnologyProcessService {
	
	@Autowired
	private TechnologyProcessDao processDao;
	@Autowired
	private TechnologyProcessProcedureDao procedureDao ;
	@Autowired
	private TechnologyProcessProcedureNodeDao nodeDao ;
	@Autowired
	private FileTransferService fileTransferService;
	@Autowired
	private FlowInstanceService flowInstanceService;
	@Autowired
	private ContextService contextService;
	@Autowired
	private SysConfigService sysConfigService;
	@Autowired
	private SysRelationDao sysRelationDao;
	@Autowired
	private SysAttachmentService sysAttachmentService;
    @Autowired
    private TechnologyProcessProcedureNodeDao technologyProcessProcedureNodeDao;

	@Override
	public String save(TechnologyProcess model) {
		return saveOrUpdate(model);
	}
	
	@Override
	public void update(TechnologyProcess model) {
		saveOrUpdate(model);
	}

	@Override
	public String saveOrUpdate(TechnologyProcess model) {
		if (StrUtil.isBlank(model.getId())) {
			doSave(model) ;
		}else{
			doUpdate(model) ;
		}
		return model.getId() ;
	}
	
	private void doSave(TechnologyProcess model) {
		String bisType = SysBillConstants.mds_technology_process;
		String flowType = SysBillConstants.mds_technology_process;
		if ("a".equals(model.getType())){
			bisType = SysBillConstants.mds_technology_process_a;
		}else if("j".equals(model.getType())){
			bisType = SysBillConstants.mds_technology_process_j;
		}else if("r".equals(model.getType())){
			bisType = SysBillConstants.mds_technology_process_r;
		}else if("s".equals(model.getType())){
			bisType = SysBillConstants.mds_technology_process_s;
		}else if ("n".equals(model.getType())&& "1".equals(model.getListType())){
			bisType = SysBillConstants.mds_technology_process_sim;
			flowType = SysBillConstants.mds_technology_process_sim;
		}
		model.setId(IdManager.nextId());
		model.setDeptId(contextService.getCurrentDeptId());
		model.setBisType(bisType);
		model.setDate(DateUtils.getDate(new Date(), "yyyyMMdd"));
		model.setStatus(OrderStatusEnum.EDIT.getCode());
		
		processDao.save(model) ;
		doItemSave(model);
		
		FlowFormModel handler = new FlowFormModel();
		handler.setOrderId(model.getId());
		handler.setOrderType(model.getBisType());
		handler.setOrderDate(model.getDate());
		handler.setOrderRemarks(model.getRemarks());
		handler.setOrderCreatedAt(new Date());
		handler.setOrderCreatedBy(SecurityUtils.getUserId());
		handler.setOrderService(TechnologyProcessService.class.getName());
		handler.setOrderDeptId(model.getDeptId());

		Map<String, Object> variables = new HashMap<>();
		variables.put("bisType", model.getBisType());
		variables.put("deptId", model.getDeptId());
		handler.setVariables(variables);

		flowInstanceService.startInstanceByBillId(flowType, Arrays.asList(handler));
	}
	private void doItemSave(TechnologyProcess model) {
		List<TechnologyProcessProcedure> proceduresList = model.getProcedures();
		if (proceduresList!=null && proceduresList.size()>0) {
			proceduresList.forEach(p->{
				p.setId(IdManager.nextId());
				p.setProcessId(model.getId());
				procedureDao.save(p);
				doPartSave(p);
			});
		}
	}
	private void doPartSave(TechnologyProcessProcedure model) {
		List<TechnologyProcessProcedureNode> nodesList = model.getNode();
		if (nodesList!=null && nodesList.size()>0) {
			nodesList.forEach(n->{
//				n.setId(IdManager.nextId());
				n.setProcessId(model.getProcessId());
				n.setProcedureId(model.getId());
				nodeDao.save(n);
			});
		}
	}
	
	private void doUpdate(TechnologyProcess model) {
		TechnologyProcess delay = processDao.getOne(new QueryWrapper<TechnologyProcess>().lambda().eq(TechnologyProcess::getId, model.getId())) ;
		if(!OrderStatusEnum.EDIT.getCode().equals(delay.getStatus())) {
			throw new ChuangkeException("非编制状态，不允许编辑") ;
		}
		processDao.updateById(model) ;
		procedureDao.remove(new QueryWrapper<TechnologyProcessProcedure>().lambda().eq(TechnologyProcessProcedure::getProcessId, model.getId()));
		nodeDao.remove(new QueryWrapper<TechnologyProcessProcedureNode>().lambda().eq(TechnologyProcessProcedureNode::getProcessId, model.getId()));
		doItemSave(model);
	}

	@Override
	public void delete(List<String> billIds) {
		QueryWrapper<TechnologyProcess> qw = new QueryWrapper<TechnologyProcess>() ;
		qw.lambda().in(TechnologyProcess::getId, billIds) ;
		List<TechnologyProcess> list = processDao.list(qw) ;
		for(TechnologyProcess delay : list) {
			if(!OrderStatusEnum.EDIT.getCode().equals(delay.getStatus())) {
				throw new ChuangkeException("只能删除编制中的业务单据") ;
			}
		}
		
        processDao.remove(new QueryWrapper<TechnologyProcess>().lambda().in(TechnologyProcess::getId, billIds));
		procedureDao.remove(new QueryWrapper<TechnologyProcessProcedure>().lambda().in(TechnologyProcessProcedure::getProcessId, billIds));
		nodeDao.remove(new QueryWrapper<TechnologyProcessProcedureNode>().lambda().in(TechnologyProcessProcedureNode::getProcessId, billIds));
        flowInstanceService.deleteInstance(billIds);
	}

	@Override
	@TransResult
	public TechnologyProcess load(String billId) {
		TechnologyProcess process = processDao.getOne(new QueryWrapper<TechnologyProcess>().lambda().eq(TechnologyProcess::getId, billId));
		List<TechnologyProcessProcedure> procedureList = procedureDao.list(new QueryWrapper<TechnologyProcessProcedure>().lambda().eq(TechnologyProcessProcedure::getProcessId, billId));
		if (procedureList != null && !procedureList.isEmpty()) {
			procedureList.forEach(p -> {
						List<TechnologyProcessProcedureNode> nodeList = nodeDao.list(new QueryWrapper<TechnologyProcessProcedureNode>().lambda().eq(TechnologyProcessProcedureNode::getProcessId, billId).eq(TechnologyProcessProcedureNode::getProcedureId, p.getId()));
						if (nodeList != null && !nodeList.isEmpty()) {
//							nodeList.forEach(n->{
//								LambdaQueryWrapper<TechnologyTypeParamConf> qw = new QueryWrapper<TechnologyTypeParamConf>().lambda().eq(TechnologyTypeParamConf::getCategory, n.getCategory()).eq(TechnologyTypeParamConf::getType,n.getType());
//								TechnologyTypeParamConf technologyTypeParamConf = paramConfDao.getOne(qw);
//								n.setTechnologyTypeParamConf(technologyTypeParamConf);
//							});
							p.setNode(nodeList);
						}
					}
			);
			process.setProcedures(procedureList);
		}
		//一个主体对应多个类型的文件，任务管理相关报告用_T,仿真用_S
		List<SysAttachment> taskFileList = sysAttachmentService.getListBySubjectId(process.getId()+"_T");
		process.setTaskFileList(taskFileList);
		List<SysAttachment> technologyFileList = sysAttachmentService.getListBySubjectId(process.getId()+"_S");
		process.setTechnologyFileList(technologyFileList);
		return process;
	}

	@Override
	@TransResult
	public IPage<TechnologyProcess> page(CkPageQuery page) {
		List<String> deptIds = contextService.getUserDataAndAuditDeptIdDetail() ;
		if(CollectionUtils.isEmpty(deptIds)) {
			return page.getPage() ; 
		}
		
		QueryWrapper<TechnologyProcess> qw = new QueryWrapper<>();
		qw.lambda().in(TechnologyProcess::getDeptId, deptIds) ;
		qw.lambda().like(TechnologyProcess::getType, page.getAsString("type")) ;
//		qw.lambda().eq(TechnologyProcess::getListType, page.getAsString("listType")) ;//更换菜单后停用
//		if(!contextService.getCurrentRoleIds().contains("1")) {
//			qw.lambda().eq(TechnologyProcess::getCreatedBy, SecurityUtils.getUserId()) ;
//		}

		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
		String startTime = (String) page.get("startTime");
		if( org.apache.commons.lang3.StringUtils.isNotBlank(startTime)) {
			LocalDate startDateTime = LocalDate.parse(startTime);
			LocalDateTime startOfDay = startDateTime.atStartOfDay();
			startTime = startOfDay.format(formatter);
			qw.lambda().ge(TechnologyProcess::getCreatedAt, startTime);
		}
		String endTime = (String) page.get("endTime");
		if(org.apache.commons.lang3.StringUtils.isNotBlank(endTime)) {
			LocalDate endDateTime = LocalDate.parse(endTime);
			LocalDateTime endOfDay = endDateTime.atTime(23, 59, 59);
			endTime = endOfDay.format(formatter);
			qw.lambda().le(TechnologyProcess::getCreatedAt, endTime);
		}
		if (!StringUtils.isEmpty(page.getAsString("filterStatus"))){
			qw.lambda().eq(TechnologyProcess::getStatus, page.getAsString("filterStatus"));
		}
		if (!StringUtils.isEmpty(page.getAsString("filterName"))){
			qw.lambda().like(TechnologyProcess::getName, page.getAsString("filterName"));
		}
		
		SysUser user= contextService.getCurrentUser();
		String userId = user.getId();
		String userDeptId = user.getDeptId();
		if (!"admin".equals(userId)) {
			qw.lambda().eq(TechnologyProcess::getDeptId, userDeptId);
		}
		qw.lambda().orderByDesc(TechnologyProcess::getCreatedAt) ;
		IPage<TechnologyProcess> resultPage = processDao.page(page.getPage(),qw);
		List<TechnologyProcess> records = resultPage.getRecords();
		DataBuilder.of(records)
				.appendBuilder(new DeptDataBuilder(), new DataBuildConfig("deptId", "deptName")).build();
		resultPage.setRecords(records);
		return resultPage;
	}

	@Override
	public FileInfo upload(MultipartFile file) {
		FileInfo fileInfo = fileTransferService.upload(file, FileConstants.SYSTEM_DBANK_ID);
		return fileInfo;
	}

	
	@Override
	public void submitBatch(List<String> orderIdList) {
    	UpdateWrapper<TechnologyProcess> uw = new UpdateWrapper<>();
		uw.set(ColumnConstants.COL_STATUS, OrderStatusEnum.APPROVAL.getCode());
		uw.in("id", orderIdList).eq(ColumnConstants.COL_STATUS, OrderStatusEnum.EDIT.getCode());
		processDao.update(uw);
	}

	@Override
	public void cancelSubmitBatch(List<String> orderIdList) {
		UpdateWrapper<TechnologyProcess> uw = new UpdateWrapper<>();
		uw.set(ColumnConstants.COL_STATUS, OrderStatusEnum.EDIT.getCode());
		List<String> status = new ArrayList<>();
		status.add(OrderStatusEnum.APPROVAL.getCode());
		status.add(OrderStatusEnum.APPROVAL_SECOND.getCode());
		status.add(OrderStatusEnum.APPROVAL_THIRD.getCode());
		status.add(OrderStatusEnum.APPROVAL_FOUR.getCode());
		uw.in("id", orderIdList).in(ColumnConstants.COL_STATUS, status);
//		uw.in("id", orderIdList).eq(ColumnConstants.COL_STATUS, OrderStatusEnum.APPROVAL.getCode());
		processDao.update(uw);
	}

	@Override
	public void rejectBatch(List<String> orderIdList) {
		UpdateWrapper<TechnologyProcess> uw = new UpdateWrapper<>();
		uw.set(ColumnConstants.COL_STATUS, OrderStatusEnum.EDIT.getCode());
		List<String> status = new ArrayList<>();
		status.add(OrderStatusEnum.APPROVAL.getCode());
		status.add(OrderStatusEnum.APPROVAL_SECOND.getCode());
		status.add(OrderStatusEnum.APPROVAL_THIRD.getCode());
		status.add(OrderStatusEnum.APPROVAL_FOUR.getCode());
		uw.in("id", orderIdList).in(ColumnConstants.COL_STATUS, status);
//		uw.in("id", orderIdList).eq(ColumnConstants.COL_STATUS, OrderStatusEnum.APPROVAL.getCode());
		processDao.update(uw);
	}

	@Override
	public void completeBatch(List<String> orderIdList) {
		UpdateWrapper<TechnologyProcess> uw = new UpdateWrapper<>();
		uw.set(ColumnConstants.COL_STATUS, OrderStatusEnum.COMPLETE.getCode());
		uw.in("id", orderIdList).eq(ColumnConstants.COL_STATUS, OrderStatusEnum.APPROVAL_FOUR.getCode());
		processDao.update(uw);
//		if (orderIdList != null && !orderIdList.isEmpty()){
//			orderIdList.forEach(orderId->{
//				taskSimulation(orderId);
//			});
//		}
	}

	@Override
	public void knowleadged(String id) {
		UpdateWrapper<TechnologyProcess> uw = new UpdateWrapper<>();
		uw.lambda().set(TechnologyProcess::getKnowledged, Masks.BOOL_YES) ;
		uw.eq("id", id);
		processDao.update(uw);
	}

	@Override
	public String returnFile(Map<String,Object> params) throws IOException {
		List<JSONObject> pyFiles = (List<JSONObject>) params.getOrDefault("pyFiles",new ArrayList<>());
		List<JSONObject> caeFiles = (List<JSONObject>) params.getOrDefault("caeFiles",new ArrayList<>());
		List<MultipartFile> pyResult = new ArrayList<>();
		List<MultipartFile> caeResult = new ArrayList<>();
		file2MultipartFile(pyFiles,pyResult);
		file2MultipartFile(caeFiles,caeResult);
		pushSimulation((String) params.get("id"),pyResult,caeResult);
		return "1";
	}
	private void file2MultipartFile(List<JSONObject> files,List<MultipartFile> result) {
		if (files != null && !files.isEmpty()){
			files.forEach(file->{
				byte[] fileInfo = fileTransferService.download(file.getString("id"), file.getString("signId"));
				String fileExtension = "",fileName = file.getString("name");
				int dotIndex = fileName.lastIndexOf('.');
				if (dotIndex > 0) {
					fileExtension = fileName.substring(dotIndex);
				}
				String contentType = detectContentType(fileExtension);
				MultipartFile mFile = new MockMultipartFile(
						fileName,             // 表单字段名
						fileName,        // 文件名（带扩展名）
						contentType,        // 内容类型
						fileInfo            // 文件字节数据
				);
				result.add(mFile);
			});
		}
	}
	private void pushSimulation(String id,List<MultipartFile> pyFiles,List<MultipartFile> caeFiles) throws IOException {
		//处理参数
		MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
		body.add("id", id);
		for (MultipartFile file : caeFiles) {
			ByteArrayResource resource = new ByteArrayResource(file.getBytes()) {
				@Override
				public String getFilename() {
					return file.getOriginalFilename(); // 保持原始文件名
				}
			};
			HttpHeaders fileHeaders = new HttpHeaders();
			fileHeaders.setContentType(MediaType.parseMediaType(file.getContentType()));
			body.add("caeFiles", new HttpEntity<>(resource, fileHeaders));
		}

		for (MultipartFile file : pyFiles) {
			ByteArrayResource resource = new ByteArrayResource(file.getBytes()) {
				@Override
				public String getFilename() {
					return file.getOriginalFilename();
				}
			};
			HttpHeaders fileHeaders = new HttpHeaders();
			fileHeaders.setContentType(MediaType.parseMediaType(file.getContentType()));
			body.add("pyFiles", new HttpEntity<>(resource, fileHeaders));
		}
		//处理url
		SysConfig sysConfig = sysConfigService.find("SYSTEM","receiveSimulation-url");
		String url = sysConfig.getParamValue();
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.MULTIPART_FORM_DATA);

		// 3. 发送请求到副应用
		HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
		RestTemplate restTemplate = new RestTemplateBuilder().build();
		ResponseEntity<String> response = restTemplate.postForEntity(
				url,
				requestEntity,
				String.class
		);
		if (!response.getStatusCode().is2xxSuccessful()) {
			throw new IOException("副应用响应错误: " + response.getBody());
		}

	}
	private String detectContentType(String fileExtension) {
		switch (fileExtension.toLowerCase()) {
			case ".jpg": case ".jpeg":
				return "image/jpeg";
			case ".png":
				return "image/png";
			case ".pdf":
				return "application/pdf";
			case ".txt":
				return "text/plain";
			case ".cae": case ".inp":
				return "application/octet-stream"; // CAE文件类型
			case ".py":
				return "text/x-python";            // Python脚本
			default:
				return "application/octet-stream"; // 默认二进制流
		}
	}

	@Override
	@TransResult
	public String taskSimulation(String billId) {
		//先处理具体单据
		TechnologyProcess process = load(billId);
		//校验
		if (process == null || !process.getStatus().equals(OrderStatusEnum.COMPLETE.getCode())){
			throw new RuntimeException("单据状态异常，请检查是否为结束状态。");
		}
		process.setId("");
		process.setListType("s");
		List<TechnologyProcessProcedure> procedureList = process.getProcedures();
		procedureList.forEach(procedure->{
			procedure.setId(IdManager.nextId());
			List<TechnologyProcessProcedureNode> nodeList = procedure.getNode();
			nodeList.forEach(node->{
				node.setId(IdManager.nextId());
			});
		});
		String newId = save(process);
		//再处理附件相关
		QueryWrapper<SysRelation> qw = new QueryWrapper<>();
		qw.lambda().eq(SysRelation::getSubjectId, billId);
		List<SysRelation> relationList = sysRelationDao.list(qw);
		relationList.forEach(relation->{
			relation.setSubjectId(newId);
			relation.setId(IdManager.nextId());
		});
		sysRelationDao.saveBatch(relationList);
		return newId;
	}
	@Override
	@TransResult
	public void update2SaveSimulate(Map<String, Object>  params) {
		String id = String.valueOf(params.get("id")==null?"":params.get("id")) ;
		if (StringUtils.isEmpty( id)){
			return;
		}
		TechnologyProcessProcedureNode node = technologyProcessProcedureNodeDao.getById(id);
		if (node == null){
			return;
		}
		String simulateInfo = (String) params.get("simulateInfo");
		node.setSimulateInfo(simulateInfo);
		technologyProcessProcedureNodeDao.updateById(node);
	}
}
